#ifndef PARSER_DATETIME_H
#define PARSER_DATETIME_H

#include <string>
#include <regex>
#include <boost\algorithm\string.hpp>
#include <boost\date_time.hpp>

using namespace std;
using namespace boost::posix_time;
using namespace boost::gregorian;

/**
 * This utility class handles the parsing of dates and times.
 * 
 * The client calls parseDateTime and parseTime. 
 * 
 * Throws 
 @author Ling Chun Kai
*/

class ParserDateTime {
public:
	ptime parseDateTime(string input);
	time_duration parseTime(string input);
	ptime parseDate(string input);
private:
	// Standard calender constant
	static const int NUM_MONTHS = 12;
	static const int NUM_HOURS = 24;
	static const int NUM_MINUTES = 60;
	static const int NUM_DAYS_IN_WEEK = 7;

	// This is the cutoff year used for short representations of years
	// Eg. 12 == 2012
	static const int CUTOFF_REF_YEAR = 2000;

	// Minimum allowed year
	static const int MIN_YEAR = 0;

	// Maximum allowed value of shortened year
	static const int MAX_SHORTENED_YEAR = 100;

	// Unique values signalling that the year given is invalid
	static const int INVALID_YEAR = -1;
	static const int INVALID_MONTH = -1;
	static const int INVALID_DAY = -1;

	static const string EMPTY_STRING;

	// Delimiters between month/day/years
	static const string DELIMITERS;

	// Delimiters between the date and time
	static const string DATE_TIME_FORMAT;
	static const string DELIMITERS_DATE_TIME;
	static const int DATE_TIME_POS_DATE = 1;
	static const int DATE_TIME_POS_TIME = 2;

	// Full date format includes the year
	static const string DELIMITED_FULL_DATE_FORMAT;
	static const int NUM_DELIMITED_FULL_DATE_FORMATS = 2;
	// used for formats day-month-year
	static const int DELIMITED_FULL_DATE_POS_YEAR[NUM_DELIMITED_FULL_DATE_FORMATS];
	static const int DELIMITED_FULL_DATE_POS_MONTH[NUM_DELIMITED_FULL_DATE_FORMATS];
	static const int DELIMITED_FULL_DATE_POS_DAY[NUM_DELIMITED_FULL_DATE_FORMATS];

	// Partial date format 
	static const string DELIMITED_PARTIAL_DATE_FORMAT;
	static const int DELIMITED_PARTIAL_DATE_POS_MONTH = 2;
	static const int DELIMITED_PARTIAL_DATE_POS_DAY = 1;

	// Allowable formats for months
	static const string MONTH_FORMATS[NUM_MONTHS];

	// Separate date from time (if applicable). Returns true if there is a 
	// possible time in the input
	bool separateDateFromTime(string input, string& date, string& time);

	// Name of days
	enum Days {SUN = 0, MON, TUE, WED, THURS, FRI, SAT};
	static const int BOOST_DAYS[NUM_DAYS_IN_WEEK];
	static const string DAY_FORMATS[NUM_DAYS_IN_WEEK];

	// Parsing functions for dates
	date parseDelimitedFullDate(string input);
	date parseDelimitedPartialDate(string input);
	date parseSpecialDate(string input);	// Parsing for special dates

	// Regex format for time represented purely in numbers
	static const string NUMERIC_TIME_FORMAT;
	static const int NUMERIC_TIME_FORMAT_POS = 1;
	// used to separate minutes and hour
	static const int NUMERIC_TIME_MODULO = 100;	

	// Parsing a time represented solely by numbers 
	time_duration parseNumericTime(string input);
	// Extract the hours and minutes in a time represented solely by numbers
	void extractHoursMinutes(int numericTime, int& hours, int& minutes);

	static const string DELIMITED_TIME_FORMAT;
	static const string DELIMITERS_TIME;
	static const int DELIMITED_TIME_FORMAT_POS_HOURS;
	static const int DELIMITED_TIME_FORMAT_POS_MINUTES;

	time_duration parseDelimitedTime(string input);
	
	// Converts the given hours and minutes into an actual time object
	time_duration computeTime(int hours, int minutes);

	// Parse individual month/day/year fields 
	int parseMonth(string input);
	int parseDay(string input);
	int parseYear(string input);

	// Extracts a decimal number from a string.
	// Returns true if successful, ie, the string is a perfect integer,
	// no other characters contaminating it
	bool extractDecimalNumber(string input, int& output);
};

#endif